/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// r_postProcess.cpp - post-process and color correction manamgent
//

// TODO:
// - Quake does not use areas, so just set one big area for each map?


#include "r_local.h"


#define POST_PROCESS_EQUAL_EPSILON			0.00001f

static postProcessParms_t	r_postProcessParmsGeneric = {1.0f, 0.5f, 1.0f, 1.0f, glqVec3(0.0f), glqVec3(1.0f), glqVec3(1.0f), glqVec3(0.0f), glqVec3(1.0f), glqVec3(1.0f), glqVec3(1.0f), {0}, "_default"};


// FIXME!!!
int CM_NumAreas (void){

	return 1;
}


/*
 ==============================================================================

 COLOR TABLE LOADING

 ==============================================================================
*/


/*
 ==================
 R_LoadColorTable
 ==================
*/
static void R_LoadColorTable (const char *name, byte colorTable[256][4]){

	tgaHeader_t	header;
	byte		*data;
	byte		*image;
	int			i;

	// If the default color table is specified
	if (!Str_ICompare(name, "_default")){
		for (i = 0; i < 256; i++){
			colorTable[i][0] = i;
			colorTable[i][1] = i;
			colorTable[i][2] = i;
			colorTable[i][3] = 255;
		}

		return;
	}

	// Load the file
	FS_ReadFile(name, (void **)&data);
	if (!data){
		Com_Printf(S_COLOR_YELLOW "WARNING: couldn't find color table '%s', using default\n", name);

		// Create a default color table
		for (i = 0; i < 256; i++){
			colorTable[i][0] = i;
			colorTable[i][1] = i;
			colorTable[i][2] = i;
			colorTable[i][3] = 255;
		}

		return;
	}

	// Parse the TGA header
	header.bIdLength = data[0];
	header.bColormapType = data[1];
	header.bImageType = data[2];
	header.wColormapIndex = data[3] | (data[4] << 8);
	header.wColormapLength = data[5] | (data[6] << 8);
	header.bColormapSize = data[7];
	header.wXOrigin = data[8] | (data[9] << 8);
	header.wYOrigin = data[10] | (data[11] << 8);
	header.wWidth = data[12] | (data[13] << 8);
	header.wHeight = data[14] | (data[15] << 8);
	header.bPixelSize = data[16];
	header.bAttributes = data[17];

	if (header.bImageType != TGA_IMAGE_TRUECOLOR && header.bImageType != TGA_IMAGE_MONOCHROME)
		Com_Error(false, "R_LoadColorTable: only type %i (RGB) and %i (gray) images supported (%s)", TGA_IMAGE_TRUECOLOR, TGA_IMAGE_MONOCHROME, name);

	if (header.bPixelSize != 8 && header.bPixelSize != 24)
		Com_Error(false, "R_LoadColorTable: only 8 (gray) and 24 (RGB) bit images supported (%s)", name);

	if (header.wWidth != 256 || header.wHeight != 1)
		Com_Error(false, "R_LoadColorTable: bad image size (%i x %i) (%s)", header.wWidth, header.wHeight, name);

	// Read the image pixels
	image = data + 18 + header.bIdLength;

	switch (header.bPixelSize){
	case 8:
		for (i = 0; i < 256; i++, image += 1){
			colorTable[i][0] = image[0];
			colorTable[i][1] = image[0];
			colorTable[i][2] = image[0];
			colorTable[i][3] = 255;
		}

		break;
	case 24:
		for (i = 0; i < 256; i++, image += 3){
			colorTable[i][0] = image[2];
			colorTable[i][1] = image[1];
			colorTable[i][2] = image[0];
			colorTable[i][3] = 255;
		}

		break;
	}

	// Free file data
	FS_FreeFile(data);
}

/*
 ==================
 R_BlendColorTables
 ==================
*/
static void R_BlendColorTables (byte colorTable[256][4], const byte previousTable[256][4], const byte currentTable[256][4], float frac){

#if defined SIMD_X86

	__m128i	xmmZero, xmmColor[2];
	__m128i	xmmScale, xmmInvScale;
	__m128i	xmmSrc, xmmSrcScaled;
	__m128i	xmmDst, xmmDstScaled;
	float	f;
	int		i;

	f = 256.0f * frac;
	i = _mm_cvtt_ss2si(_mm_load_ss(&f));

	xmmZero = _mm_setzero_si128();

	xmmScale = _mm_set1_epi16(i);
	xmmInvScale = _mm_set1_epi16(256 - i);

	for (i = 0; i < 256; i += 4){
		xmmSrc = _mm_loadu_si128((const __m128i *)(previousTable[i]));
		xmmDst = _mm_loadu_si128((const __m128i *)(currentTable[i]));

		xmmSrcScaled = _mm_mullo_epi16(_mm_unpacklo_epi8(xmmSrc, xmmZero), xmmInvScale);
		xmmDstScaled = _mm_mullo_epi16(_mm_unpacklo_epi8(xmmDst, xmmZero), xmmScale);

		xmmColor[0] = _mm_srli_epi16(_mm_add_epi16(xmmSrcScaled, xmmDstScaled), 8);

		xmmSrcScaled = _mm_mullo_epi16(_mm_unpackhi_epi8(xmmSrc, xmmZero), xmmInvScale);
		xmmDstScaled = _mm_mullo_epi16(_mm_unpackhi_epi8(xmmDst, xmmZero), xmmScale);

		xmmColor[1] = _mm_srli_epi16(_mm_add_epi16(xmmSrcScaled, xmmDstScaled), 8);

		_mm_storeu_si128((__m128i *)(colorTable[i]), _mm_packus_epi16(xmmColor[0], xmmColor[1]));
	}

#else

	int		scale, invScale;
	int		i;

	scale = glqMath::FloatToInt(256.0f * frac);
	invScale = 256 - scale;

	for (i = 0; i < 256; i++){
		colorTable[i][0] = (previousTable[i][0] * invScale + currentTable[i][0] * scale) >> 8;
		colorTable[i][1] = (previousTable[i][1] * invScale + currentTable[i][1] * scale) >> 8;
		colorTable[i][2] = (previousTable[i][2] * invScale + currentTable[i][2] * scale) >> 8;
		colorTable[i][3] = (previousTable[i][3] * invScale + currentTable[i][3] * scale) >> 8;
	}

#endif
}


/*
 ==============================================================================

 POST-PROCESSING EFFECTS PARSING

 ==============================================================================
*/


/*
 ==================
 R_ParsePostProcess
 ==================
*/
static bool R_ParsePostProcess (script_t *script){

	token_t				token;
	postProcessParms_t	*postProcessParms;
	int					area;
	int					i;

	// Parse the area number
	if (!PS_ReadInteger(script, &area)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing area number in post-process file\n");
		return false;
	}

	if (area < 0 || area >= CM_NumAreas()){
		Com_Printf(S_COLOR_YELLOW "WARNING: invalid value of %i for area number in post-process file\n", area);
		return false;
	}

	// Parse the post-process parameters
	postProcessParms = &rg.postProcess.postProcessList[area];

	if (!PS_ExpectTokenString(script, &token, "{", true)){
		Com_Printf(S_COLOR_YELLOW "WARNING: expected '{', found '%s' instead in post-process file (area %i)\n", token.string, area);
		return false;
	}

	while (1){
		if (!PS_ReadToken(script, &token)){
			Com_Printf(S_COLOR_YELLOW "WARNING: no concluding '}' in post-process file (area %i)\n", area);
			return false;	// End of script
		}

		if (!Str_ICompare(token.string, "}"))
			break;			// End of post-process

		// Parse the parameter
		if (!Str_ICompare(token.string, "bloomContrast")){
			if (!PS_ReadFloat(script, &postProcessParms->bloomContrast)){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'bloomContrast' parameter in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "bloomThreshold")){
			if (!PS_ReadFloat(script, &postProcessParms->bloomThreshold)){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'bloomThreshold' parameter in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "baseIntensity")){
			if (!PS_ReadFloat(script, &postProcessParms->baseIntensity)){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'baseIntensity' parameter in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "glowIntensity")){
			if (!PS_ReadFloat(script, &postProcessParms->glowIntensity)){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'glowIntensity' parameter in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "colorShadows")){
			if (!PS_ReadMatrix1D(script, 3, postProcessParms->colorShadows.ToFloatPtr())){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'colorShadows' parameters in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "colorHighlights")){
			if (!PS_ReadMatrix1D(script, 3, postProcessParms->colorHighlights.ToFloatPtr())){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'colorHighlights' parameters in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "colorMidtones")){
			if (!PS_ReadMatrix1D(script, 3, postProcessParms->colorMidtones.ToFloatPtr())){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'colorMidtones' parameters in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "colorMinOutput")){
			if (!PS_ReadMatrix1D(script, 3, postProcessParms->colorMinOutput.ToFloatPtr())){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'colorMinOutput' parameter in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "colorMaxOutput")){
			if (!PS_ReadMatrix1D(script, 3, postProcessParms->colorMaxOutput.ToFloatPtr())){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'colorMaxOutput' parameter in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "colorSaturation")){
			if (!PS_ReadMatrix1D(script, 3, postProcessParms->colorSaturation.ToFloatPtr())){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'colorSaturation' parameter in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "colorTint")){
			if (!PS_ReadMatrix1D(script, 3, postProcessParms->colorTint.ToFloatPtr())){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'colorTint' parameters in post-process file (area %i)\n", area);
				return false;
			}
		}
		else if (!Str_ICompare(token.string, "colorTable")){
			if (!PS_ReadToken(script, &token)){
				Com_Printf(S_COLOR_YELLOW "WARNING: missing 'colorTable' parameter in post-process file (area %i)\n", area);
				return false;
			}

			Str_Copy(postProcessParms->colorTableName, token.string, sizeof(postProcessParms->colorTableName));
		}
		else {
			Com_Printf(S_COLOR_YELLOW "WARNING: unknown parameter '%s' in post-process file (area %i)\n", token.string, area);
			return false;
		}
	}

	// Clamp the post-process parameters
	postProcessParms->bloomContrast = glqMath::ClampFloat(postProcessParms->bloomContrast, 0.0f, 4.0f);
	postProcessParms->bloomThreshold = glqMath::ClampFloat(postProcessParms->bloomThreshold, 0.0f, 1.0f);
	postProcessParms->baseIntensity = glqMath::ClampFloat(postProcessParms->baseIntensity, 0.0f, 4.0f);
	postProcessParms->glowIntensity = glqMath::ClampFloat(postProcessParms->glowIntensity, 0.0f, 4.0f);
	postProcessParms->colorShadows.Clamp(0.0f, 1.0f);
	postProcessParms->colorHighlights.Clamp(0.0f, 1.0f);
	postProcessParms->colorMidtones.Clamp(0.1f, 9.99f);
	postProcessParms->colorMinOutput.Clamp(0.0f, 1.0f);
	postProcessParms->colorMaxOutput.Clamp(0.0f, 1.0f);
	postProcessParms->colorSaturation.Clamp(0.0f, 4.0f);
	postProcessParms->colorTint.Clamp(0.0f, 1.0f);

	for (i = 0; i < 3; i++){
		if (postProcessParms->colorShadows[i] >= postProcessParms->colorHighlights[i]){
			postProcessParms->colorShadows[i] = 0.0f;
			postProcessParms->colorHighlights[i] = 1.0f;
		}

		if (postProcessParms->colorMinOutput[i] >= postProcessParms->colorMaxOutput[i]){
			postProcessParms->colorMinOutput[i] = 0.0f;
			postProcessParms->colorMaxOutput[i] = 1.0f;
		}
	}

	// Load the color table
	R_LoadColorTable(postProcessParms->colorTableName, postProcessParms->colorTable);

	return true;
}

/*
 ==================
 R_LoadPostProcess
 ==================
*/
void R_LoadPostProcess (const char *name){

	script_t	*script;
	token_t		token;

	if (!r_postProcess->integerValue)
		return;

	// Copy the name
	Str_Copy(rg.postProcess.postProcessName, name, sizeof(rg.postProcess.postProcessName));

	// Load the script file
	script = PS_LoadScriptFile(name);
	if (!script){
		Com_Printf(S_COLOR_RED "Post-process file %s not found\n", name);
		return;
	}

	PS_SetScriptFlags(script, SF_NOWARNINGS | SF_NOERRORS | SF_ALLOWPATHNAMES);

	// Parse it
	while (1){
		if (!PS_ReadToken(script, &token))
			break;		// End of script

		if (!Str_ICompare(token.string, "postProcess")){
			if (!R_ParsePostProcess(script))
				break;
		}
		else {
			Com_Printf(S_COLOR_YELLOW "WARNING: expected 'postProcess', found '%s' instead in post-process file\n", token.string);
			break;
		}
	}

	// Free the script file
	PS_FreeScript(script);
}


/*
 ==============================================================================

 INTEGRATED POST-PROCESSING EFFECTS EDITOR

 ==============================================================================
*/

static void *					R_CreatePostProcessEditorWindow ();
static void						R_DestroyPostProcessEditorWindow ();

static void						R_EditPostProcessParameters (int index, postProcessParms_t *parms);


/*
 ==================
 R_EditAreaPostProcess
 ==================
*/
void R_EditAreaPostProcess (int area){

	if (!rg.postProcess.editing)
		return;		// Not active

	if (rg.postProcess.editingArea == area)
		return;		// Area hasn't changed
	rg.postProcess.editingArea = area;

	// Edit the specified area post-process parameters
	if (area == -1){
		R_EditPostProcessParameters(-1, NULL);
		return;
	}

	R_EditPostProcessParameters(area, &rg.postProcess.postProcessList[area]);
}

/*
 ==================
 R_PostProcessEditorUpdateCallback
 ==================
*/
static void R_PostProcessEditorUpdateCallback (int index, postProcessParms_t *parms){

	postProcessParms_t	*postProcessParms;
	int					i;

	if (!rg.postProcess.editing)
		return;		// Not active

	if (index < 0 || index >= CM_NumAreas())
		return;		// Out of range

	postProcessParms = &rg.postProcess.postProcessList[index];

	// Copy the parameters
	Mem_Copy(postProcessParms, parms, sizeof(postProcessParms_t));

	// Clamp the parameters
	postProcessParms->bloomContrast = glqMath::ClampFloat(postProcessParms->bloomContrast, 0.0f, 4.0f);
	postProcessParms->bloomThreshold = glqMath::ClampFloat(postProcessParms->bloomThreshold, 0.0f, 1.0f);
	postProcessParms->baseIntensity = glqMath::ClampFloat(postProcessParms->baseIntensity, 0.0f, 4.0f);
	postProcessParms->glowIntensity = glqMath::ClampFloat(postProcessParms->glowIntensity, 0.0f, 4.0f);
	postProcessParms->colorShadows.Clamp(0.0f, 1.0f);
	postProcessParms->colorHighlights.Clamp(0.0f, 1.0f);
	postProcessParms->colorMidtones.Clamp(0.1f, 9.99f);
	postProcessParms->colorMinOutput.Clamp(0.0f, 1.0f);
	postProcessParms->colorMaxOutput.Clamp(0.0f, 1.0f);
	postProcessParms->colorSaturation.Clamp(0.0f, 4.0f);
	postProcessParms->colorTint.Clamp(0.0f, 1.0f);

	for (i = 0; i < 3; i++){
		if (postProcessParms->colorShadows[i] >= postProcessParms->colorHighlights[i]){
			postProcessParms->colorShadows[i] = 0.0f;
			postProcessParms->colorHighlights[i] = 1.0f;
		}

		if (postProcessParms->colorMinOutput[i] >= postProcessParms->colorMaxOutput[i]){
			postProcessParms->colorMinOutput[i] = 0.0f;
			postProcessParms->colorMaxOutput[i] = 1.0f;
		}
	}

	// Load the color table
	R_LoadColorTable(postProcessParms->colorTableName, postProcessParms->colorTable);

	// Force an update
	rg.postProcess.time = 0;

	Mem_Copy(&rg.postProcess.previous, &r_postProcessParmsGeneric, sizeof(postProcessParms_t));
	rg.postProcess.current = NULL;
}

/*
 ==================
 R_PostProcessEditorSaveCallback
 ==================
*/
static void R_PostProcessEditorSaveCallback (){

	fileHandle_t		f;
	postProcessParms_t	*postProcessParms;
	int					i;

	if (!rg.postProcess.editing)
		return;		// Not active

	if (!rg.postProcess.postProcessName[0]){
		Com_Printf("No post-process file name available\n");
		return;
	}

	// Write the post-process file
	FS_OpenFile(rg.postProcess.postProcessName, FS_WRITE, &f);
	if (!f){
		Com_Printf("Couldn't write post-process file %s\n", rg.postProcess.postProcessName);
		return;
	}

	for (i = 0, postProcessParms = rg.postProcess.postProcessList; i < CM_NumAreas(); i++, postProcessParms++){
		FS_Printf(f, "postProcess %i" NEWLINE, i);
		FS_Printf(f, "{" NEWLINE);
		FS_Printf(f, "bloomContrast       %g" NEWLINE, postProcessParms->bloomContrast);
		FS_Printf(f, "bloomThreshold     %g" NEWLINE, postProcessParms->bloomThreshold);
		FS_Printf(f, "baseIntensity       %g" NEWLINE, postProcessParms->baseIntensity);
		FS_Printf(f, "glowIntensity       %g" NEWLINE, postProcessParms->glowIntensity);
		FS_Printf(f, "colorShadows        ( %g %g %g )" NEWLINE, postProcessParms->colorShadows[0], postProcessParms->colorShadows[1], postProcessParms->colorShadows[2]);
		FS_Printf(f, "colorHighlights     ( %g %g %g )" NEWLINE, postProcessParms->colorHighlights[0], postProcessParms->colorHighlights[1], postProcessParms->colorHighlights[2]);
		FS_Printf(f, "colorMidtones       ( %g %g %g )" NEWLINE, postProcessParms->colorMidtones[0], postProcessParms->colorMidtones[1], postProcessParms->colorMidtones[2]);
		FS_Printf(f, "colorMinOutput      ( %g %g %g )" NEWLINE, postProcessParms->colorMinOutput[0], postProcessParms->colorMinOutput[1], postProcessParms->colorMinOutput[2]);
		FS_Printf(f, "colorMaxOutput      ( %g %g %g )" NEWLINE, postProcessParms->colorMaxOutput[0], postProcessParms->colorMaxOutput[1], postProcessParms->colorMaxOutput[2]);
		FS_Printf(f, "colorSaturation     ( %g %g %g )" NEWLINE, postProcessParms->colorSaturation[0], postProcessParms->colorSaturation[1], postProcessParms->colorSaturation[2]);
		FS_Printf(f, "colorTint           ( %g %g %g )" NEWLINE, postProcessParms->colorTint[0], postProcessParms->colorTint[1], postProcessParms->colorTint[2]);
		FS_Printf(f, "colorTable          %s" NEWLINE, postProcessParms->colorTableName);
		FS_Printf(f, "}" NEWLINE);

		if (i < CM_NumAreas() - 1)
			FS_Printf(f, NEWLINE);
	}

	FS_CloseFile(f);

	Com_Printf("Wrote post-process file %s with %i areas\n", rg.postProcess.postProcessName, CM_NumAreas());
}

/*
 ==================
 R_PostProcessEditorCloseCallback
 ==================
*/
static void R_PostProcessEditorCloseCallback (){

	if (!rg.postProcess.editing)
		return;		// Not active

	rg.postProcess.editing = false;
	rg.postProcess.editingArea = -1;

	// Close the post-process editor
	Com_CloseEditor();
}

/*
 ==================
 R_EditPostProcess_f
 ==================
*/
static void R_EditPostProcess_f (){

	editorCallbacks_t	callbacks;

	if (!r_postProcess->integerValue){
		Com_Printf("You must enable post-process effects to launch the post-process editor\n");
		return;
	}

	// Launch the post-process editor
	callbacks.createWindow = R_CreatePostProcessEditorWindow;
	callbacks.destroyWindow = R_DestroyPostProcessEditorWindow;
	callbacks.mouseEvent = NULL;

	if (!Com_LaunchEditor("post-process", &callbacks))
		return;

	rg.postProcess.editing = true;
	rg.postProcess.editingArea = rg.viewArea;

	// Edit the current area post-process parameters
	if (rg.viewArea == -1){
		R_EditPostProcessParameters(-1, NULL);
		return;
	}

	R_EditPostProcessParameters(rg.viewArea, &rg.postProcess.postProcessList[rg.viewArea]);
}


/*
 ==============================================================================

 PLATFORM SPECIFIC EDITOR FUNCTIONS

 ==============================================================================
*/

#ifdef _WIN32

#define POST_PROCESS_EDITOR_WINDOW_NAME		ENGINE_NAME " Post-Process Editor"
#define POST_PROCESS_EDITOR_WINDOW_CLASS	ENGINE_NAME " Post-Process Editor"
#define POST_PROCESS_EDITOR_WINDOW_STYLE	(WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX)

static postProcessParms_t		r_postProcessParms = {1.0f, 0.5f, 1.0f, 1.0f, glqVec3(0.0f), glqVec3(1.0f), glqVec3(1.0f), glqVec3(0.0f), glqVec3(1.0f), glqVec3(1.0f), glqVec3(1.0f), {0}, "_default"};

struct postProcessEditor_t {
	bool						initialized;
	bool						enabled;

	int							editIndex;
	postProcessParms_t			editParms;

	// Window stuff
	HINSTANCE					hInstance;

	HWND						hWnd;
	HWND						hWndArea;
	HWND						hWndAreaValue;
	HWND						hWndApplyToAll;
	HWND						hWndBloom;
	HWND						hWndBloomContrast;
	HWND						hWndBloomContrastValue;
	HWND						hWndBloomContrastSpin;
	HWND						hWndBloomThreshold;
	HWND						hWndBloomThresholdValue;
	HWND						hWndBloomThresholdSpin;
	HWND						hWndBaseIntensity;
	HWND						hWndBaseIntensityValue;
	HWND						hWndBaseIntensitySpin;
	HWND						hWndGlowIntensity;
	HWND						hWndGlowIntensityValue;
	HWND						hWndGlowIntensitySpin;
	HWND						hWndColorCorrection;
	HWND						hWndR;
	HWND						hWndG;
	HWND						hWndB;
	HWND						hWndColorShadows;
	HWND						hWndColorShadowsRValue;
	HWND						hWndColorShadowsRSpin;
	HWND						hWndColorShadowsGValue;
	HWND						hWndColorShadowsGSpin;
	HWND						hWndColorShadowsBValue;
	HWND						hWndColorShadowsBSpin;
	HWND						hWndColorHighlights;
	HWND						hWndColorHighlightsRValue;
	HWND						hWndColorHighlightsRSpin;
	HWND						hWndColorHighlightsGValue;
	HWND						hWndColorHighlightsGSpin;
	HWND						hWndColorHighlightsBValue;
	HWND						hWndColorHighlightsBSpin;
	HWND						hWndColorMidtones;
	HWND						hWndColorMidtonesRValue;
	HWND						hWndColorMidtonesRSpin;
	HWND						hWndColorMidtonesGValue;
	HWND						hWndColorMidtonesGSpin;
	HWND						hWndColorMidtonesBValue;
	HWND						hWndColorMidtonesBSpin;
	HWND						hWndColorMinOutput;
	HWND						hWndColorMinOutputRValue;
	HWND						hWndColorMinOutputRSpin;
	HWND						hWndColorMinOutputGValue;
	HWND						hWndColorMinOutputGSpin;
	HWND						hWndColorMinOutputBValue;
	HWND						hWndColorMinOutputBSpin;
	HWND						hWndColorMaxOutput;
	HWND						hWndColorMaxOutputRValue;
	HWND						hWndColorMaxOutputRSpin;
	HWND						hWndColorMaxOutputGValue;
	HWND						hWndColorMaxOutputGSpin;
	HWND						hWndColorMaxOutputBValue;
	HWND						hWndColorMaxOutputBSpin;
	HWND						hWndColorSaturation;
	HWND						hWndColorSaturationRValue;
	HWND						hWndColorSaturationRSpin;
	HWND						hWndColorSaturationGValue;
	HWND						hWndColorSaturationGSpin;
	HWND						hWndColorSaturationBValue;
	HWND						hWndColorSaturationBSpin;
	HWND						hWndColorTint;
	HWND						hWndColorTintRValue;
	HWND						hWndColorTintRSpin;
	HWND						hWndColorTintGValue;
	HWND						hWndColorTintGSpin;
	HWND						hWndColorTintBValue;
	HWND						hWndColorTintBSpin;
	HWND						hWndColorTable;
	HWND						hWndColorTableValue;
	HWND						hWndApply;
	HWND						hWndReset;
	HWND						hWndDefaults;
	HWND						hWndSave;

	HFONT						hFont;
};

static postProcessEditor_t		r_postProcessEditor;


/*
 ==================
 R_ApplyPostProcessParameters
 ==================
*/
static void R_ApplyPostProcessParameters (bool all){

	postProcessParms_t	parms;
	char				string[64];
	int					i;

	if (!r_postProcessEditor.enabled)
		return;

	// Read the controls
	if (GetWindowText(r_postProcessEditor.hWndBloomContrastValue, string, sizeof(string)))
		parms.bloomContrast = Str_ToFloat(string);
	else
		parms.bloomContrast = r_postProcessEditor.editParms.bloomContrast;

	if (GetWindowText(r_postProcessEditor.hWndBloomThresholdValue, string, sizeof(string)))
		parms.bloomThreshold = Str_ToFloat(string);
	else
		parms.bloomThreshold = r_postProcessEditor.editParms.bloomThreshold;

	if (GetWindowText(r_postProcessEditor.hWndBaseIntensityValue, string, sizeof(string)))
		parms.baseIntensity = Str_ToFloat(string);
	else
		parms.baseIntensity = r_postProcessEditor.editParms.baseIntensity;

	if (GetWindowText(r_postProcessEditor.hWndGlowIntensityValue, string, sizeof(string)))
		parms.glowIntensity = Str_ToFloat(string);
	else
		parms.glowIntensity = r_postProcessEditor.editParms.glowIntensity;

	if (GetWindowText(r_postProcessEditor.hWndColorShadowsRValue, string, sizeof(string)))
		parms.colorShadows[0] = Str_ToFloat(string);
	else
		parms.colorShadows[0] = r_postProcessEditor.editParms.colorShadows[0];

	if (GetWindowText(r_postProcessEditor.hWndColorShadowsGValue, string, sizeof(string)))
		parms.colorShadows[1] = Str_ToFloat(string);
	else
		parms.colorShadows[1] = r_postProcessEditor.editParms.colorShadows[1];

	if (GetWindowText(r_postProcessEditor.hWndColorShadowsBValue, string, sizeof(string)))
		parms.colorShadows[2] = Str_ToFloat(string);
	else
		parms.colorShadows[2] = r_postProcessEditor.editParms.colorShadows[2];

	if (GetWindowText(r_postProcessEditor.hWndColorHighlightsRValue, string, sizeof(string)))
		parms.colorHighlights[0] = Str_ToFloat(string);
	else
		parms.colorHighlights[0] = r_postProcessEditor.editParms.colorHighlights[0];

	if (GetWindowText(r_postProcessEditor.hWndColorHighlightsGValue, string, sizeof(string)))
		parms.colorHighlights[1] = Str_ToFloat(string);
	else
		parms.colorHighlights[1] = r_postProcessEditor.editParms.colorHighlights[1];

	if (GetWindowText(r_postProcessEditor.hWndColorHighlightsBValue, string, sizeof(string)))
		parms.colorHighlights[2] = Str_ToFloat(string);
	else
		parms.colorHighlights[2] = r_postProcessEditor.editParms.colorHighlights[2];

	if (GetWindowText(r_postProcessEditor.hWndColorMidtonesRValue, string, sizeof(string)))
		parms.colorMidtones[0] = Str_ToFloat(string);
	else
		parms.colorMidtones[0] = r_postProcessEditor.editParms.colorMidtones[0];

	if (GetWindowText(r_postProcessEditor.hWndColorMidtonesGValue, string, sizeof(string)))
		parms.colorMidtones[1] = Str_ToFloat(string);
	else
		parms.colorMidtones[1] = r_postProcessEditor.editParms.colorMidtones[1];

	if (GetWindowText(r_postProcessEditor.hWndColorMidtonesBValue, string, sizeof(string)))
		parms.colorMidtones[2] = Str_ToFloat(string);
	else
		parms.colorMidtones[2] = r_postProcessEditor.editParms.colorMidtones[2];

	if (GetWindowText(r_postProcessEditor.hWndColorMinOutputRValue, string, sizeof(string)))
		parms.colorMinOutput[0] = Str_ToFloat(string);
	else
		parms.colorMinOutput[0] = r_postProcessEditor.editParms.colorMinOutput[0];

	if (GetWindowText(r_postProcessEditor.hWndColorMinOutputGValue, string, sizeof(string)))
		parms.colorMinOutput[1] = Str_ToFloat(string);
	else
		parms.colorMinOutput[1] = r_postProcessEditor.editParms.colorMinOutput[1];

	if (GetWindowText(r_postProcessEditor.hWndColorMinOutputBValue, string, sizeof(string)))
		parms.colorMinOutput[2] = Str_ToFloat(string);
	else
		parms.colorMinOutput[2] = r_postProcessEditor.editParms.colorMinOutput[2];

	if (GetWindowText(r_postProcessEditor.hWndColorMaxOutputRValue, string, sizeof(string)))
		parms.colorMaxOutput[0] = Str_ToFloat(string);
	else
		parms.colorMaxOutput[0] = r_postProcessEditor.editParms.colorMaxOutput[0];

	if (GetWindowText(r_postProcessEditor.hWndColorMaxOutputGValue, string, sizeof(string)))
		parms.colorMaxOutput[1] = Str_ToFloat(string);
	else
		parms.colorMaxOutput[1] = r_postProcessEditor.editParms.colorMaxOutput[1];

	if (GetWindowText(r_postProcessEditor.hWndColorMaxOutputBValue, string, sizeof(string)))
		parms.colorMaxOutput[2] = Str_ToFloat(string);
	else
		parms.colorMaxOutput[2] = r_postProcessEditor.editParms.colorMaxOutput[2];

	if (GetWindowText(r_postProcessEditor.hWndColorSaturationRValue, string, sizeof(string)))
		parms.colorSaturation[0] = Str_ToFloat(string);
	else
		parms.colorSaturation[0] = r_postProcessEditor.editParms.colorSaturation[0];

	if (GetWindowText(r_postProcessEditor.hWndColorSaturationGValue, string, sizeof(string)))
		parms.colorSaturation[1] = Str_ToFloat(string);
	else
		parms.colorSaturation[1] = r_postProcessEditor.editParms.colorSaturation[1];

	if (GetWindowText(r_postProcessEditor.hWndColorSaturationBValue, string, sizeof(string)))
		parms.colorSaturation[2] = Str_ToFloat(string);
	else
		parms.colorSaturation[2] = r_postProcessEditor.editParms.colorSaturation[2];

	if (GetWindowText(r_postProcessEditor.hWndColorTintRValue, string, sizeof(string)))
		parms.colorTint[0] = Str_ToFloat(string);
	else
		parms.colorTint[0] = r_postProcessEditor.editParms.colorTint[0];

	if (GetWindowText(r_postProcessEditor.hWndColorTintGValue, string, sizeof(string)))
		parms.colorTint[1] = Str_ToFloat(string);
	else
		parms.colorTint[1] = r_postProcessEditor.editParms.colorTint[1];

	if (GetWindowText(r_postProcessEditor.hWndColorTintBValue, string, sizeof(string)))
		parms.colorTint[2] = Str_ToFloat(string);
	else
		parms.colorTint[2] = r_postProcessEditor.editParms.colorTint[2];

	if (!GetWindowText(r_postProcessEditor.hWndColorTableValue, parms.colorTableName, sizeof(parms.colorTableName)))
		Str_Copy(parms.colorTableName, "_default", sizeof(parms.colorTableName));

	// Update the parameters
	if (!all){
		R_PostProcessEditorUpdateCallback(r_postProcessEditor.editIndex, &parms);
		return;
	}

	for (i = 0; i < CM_NumAreas(); i++)
		R_PostProcessEditorUpdateCallback(i, &parms);
}

/*
 ==================
 R_ResetPostProcessParameters
 ==================
*/
static void R_ResetPostProcessParameters (postProcessParms_t *parms){

	if (!r_postProcessEditor.enabled)
		return;

	// Update the controls
	SetWindowText(r_postProcessEditor.hWndBloomContrastValue, Str_FromFloat(parms->bloomContrast, -1));
	SetWindowText(r_postProcessEditor.hWndBloomThresholdValue, Str_FromFloat(parms->bloomThreshold, -1));
	SetWindowText(r_postProcessEditor.hWndBaseIntensityValue, Str_FromFloat(parms->baseIntensity, -1));
	SetWindowText(r_postProcessEditor.hWndGlowIntensityValue, Str_FromFloat(parms->glowIntensity, -1));

	SetWindowText(r_postProcessEditor.hWndColorShadowsRValue, Str_FromFloat(parms->colorShadows[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorShadowsGValue, Str_FromFloat(parms->colorShadows[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorShadowsBValue, Str_FromFloat(parms->colorShadows[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorHighlightsRValue, Str_FromFloat(parms->colorHighlights[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorHighlightsGValue, Str_FromFloat(parms->colorHighlights[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorHighlightsBValue, Str_FromFloat(parms->colorHighlights[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorMidtonesRValue, Str_FromFloat(parms->colorMidtones[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorMidtonesGValue, Str_FromFloat(parms->colorMidtones[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorMidtonesBValue, Str_FromFloat(parms->colorMidtones[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorMinOutputRValue, Str_FromFloat(parms->colorMinOutput[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorMinOutputGValue, Str_FromFloat(parms->colorMinOutput[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorMinOutputBValue, Str_FromFloat(parms->colorMinOutput[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorMaxOutputRValue, Str_FromFloat(parms->colorMaxOutput[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorMaxOutputGValue, Str_FromFloat(parms->colorMaxOutput[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorMaxOutputBValue, Str_FromFloat(parms->colorMaxOutput[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorSaturationRValue, Str_FromFloat(parms->colorSaturation[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorSaturationGValue, Str_FromFloat(parms->colorSaturation[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorSaturationBValue, Str_FromFloat(parms->colorSaturation[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorTintRValue, Str_FromFloat(parms->colorTint[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorTintGValue, Str_FromFloat(parms->colorTint[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorTintBValue, Str_FromFloat(parms->colorTint[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorTableValue, parms->colorTableName);

	// Update the parameters
	R_PostProcessEditorUpdateCallback(r_postProcessEditor.editIndex, parms);
}

/*
 ==================
 R_PostProcessEditorWindowProc
 ==================
*/
static LRESULT CALLBACK R_PostProcessEditorWindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){

	char	string[64];
	int		result;

	switch (uMsg){
	case WM_CLOSE:
		R_ApplyPostProcessParameters(false);

		R_PostProcessEditorCloseCallback();

		break;
	case WM_SYSCOMMAND:
		if (wParam == SC_KEYMENU)
			return 0;

		break;
	case WM_COMMAND:
		if (HIWORD(wParam) == BN_CLICKED){
			if ((HWND)lParam == r_postProcessEditor.hWndApplyToAll){
				R_ApplyPostProcessParameters(true);
				break;
			}

			if ((HWND)lParam == r_postProcessEditor.hWndApply){
				R_ApplyPostProcessParameters(false);
				break;
			}

			if ((HWND)lParam == r_postProcessEditor.hWndReset){
				R_ResetPostProcessParameters(&r_postProcessEditor.editParms);
				break;
			}

			if ((HWND)lParam == r_postProcessEditor.hWndDefaults){
				R_ResetPostProcessParameters(&r_postProcessParms);
				break;
			}

			if ((HWND)lParam == r_postProcessEditor.hWndSave){
				R_ApplyPostProcessParameters(false);

				R_PostProcessEditorSaveCallback();

				break;
			}
		}

		if (HIWORD(wParam) == CBN_SELCHANGE){
			if ((HWND)lParam == r_postProcessEditor.hWndAreaValue){
				result = SendMessage(r_postProcessEditor.hWndAreaValue, CB_GETCURSEL, 0, 0) - 1;

				if (result < -1 || result >= CM_NumAreas())
					break;

				R_EditAreaPostProcess(result);

				break;
			}
		}

		break;
	case WM_NOTIFY:
		if (((LPNMHDR)lParam)->code == UDN_DELTAPOS){
			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndBloomContrastSpin){
				GetWindowText(r_postProcessEditor.hWndBloomContrastValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndBloomContrastValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 4.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndBloomContrastValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 4.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndBloomThresholdSpin){
				GetWindowText(r_postProcessEditor.hWndBloomThresholdValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndBloomThresholdValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndBloomThresholdValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndBaseIntensitySpin){
				GetWindowText(r_postProcessEditor.hWndBaseIntensityValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndBaseIntensityValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 4.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndBaseIntensityValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 4.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndGlowIntensitySpin){
				GetWindowText(r_postProcessEditor.hWndGlowIntensityValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndGlowIntensityValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 4.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndGlowIntensityValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 4.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorShadowsRSpin){
				GetWindowText(r_postProcessEditor.hWndColorShadowsRValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorShadowsRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorShadowsRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorShadowsGSpin){
				GetWindowText(r_postProcessEditor.hWndColorShadowsGValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorShadowsGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorShadowsGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorShadowsBSpin){
				GetWindowText(r_postProcessEditor.hWndColorShadowsBValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorShadowsBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorShadowsBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorHighlightsRSpin){
				GetWindowText(r_postProcessEditor.hWndColorHighlightsRValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorHighlightsRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorHighlightsRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorHighlightsGSpin){
				GetWindowText(r_postProcessEditor.hWndColorHighlightsGValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorHighlightsGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorHighlightsGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorHighlightsBSpin){
				GetWindowText(r_postProcessEditor.hWndColorHighlightsBValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorHighlightsBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorHighlightsBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorMidtonesRSpin){
				GetWindowText(r_postProcessEditor.hWndColorMidtonesRValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorMidtonesRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.1f, 9.99f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorMidtonesRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.1f, 9.99f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorMidtonesGSpin){
				GetWindowText(r_postProcessEditor.hWndColorMidtonesGValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorMidtonesGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.1f, 9.99f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorMidtonesGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.1f, 9.99f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorMidtonesBSpin){
				GetWindowText(r_postProcessEditor.hWndColorMidtonesBValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorMidtonesBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.1f, 9.99f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorMidtonesBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.1f, 9.99f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorMinOutputRSpin){
				GetWindowText(r_postProcessEditor.hWndColorMinOutputRValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorMinOutputRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorMinOutputRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorMinOutputGSpin){
				GetWindowText(r_postProcessEditor.hWndColorMinOutputGValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorMinOutputGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorMinOutputGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorMinOutputBSpin){
				GetWindowText(r_postProcessEditor.hWndColorMinOutputBValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorMinOutputBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorMinOutputBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorMaxOutputRSpin){
				GetWindowText(r_postProcessEditor.hWndColorMaxOutputRValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorMaxOutputRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorMaxOutputRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorMaxOutputGSpin){
				GetWindowText(r_postProcessEditor.hWndColorMaxOutputGValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorMaxOutputGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorMaxOutputGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorMaxOutputBSpin){
				GetWindowText(r_postProcessEditor.hWndColorMaxOutputBValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorMaxOutputBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorMaxOutputBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorSaturationRSpin){
				GetWindowText(r_postProcessEditor.hWndColorSaturationRValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorSaturationRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 4.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorSaturationRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 4.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorSaturationGSpin){
				GetWindowText(r_postProcessEditor.hWndColorSaturationGValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorSaturationGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 4.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorSaturationGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 4.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorSaturationBSpin){
				GetWindowText(r_postProcessEditor.hWndColorSaturationBValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorSaturationBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 4.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorSaturationBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 4.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorTintRSpin){
				GetWindowText(r_postProcessEditor.hWndColorTintRValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorTintRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorTintRValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorTintGSpin){
				GetWindowText(r_postProcessEditor.hWndColorTintGValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorTintGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorTintGValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}

			if (((LPNMHDR)lParam)->hwndFrom == r_postProcessEditor.hWndColorTintBSpin){
				GetWindowText(r_postProcessEditor.hWndColorTintBValue, string, sizeof(string));

				if (((LPNMUPDOWN)lParam)->iDelta < 0)
					SetWindowText(r_postProcessEditor.hWndColorTintBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) + 0.01f, 0.0f, 1.0f), -1));
				else
					SetWindowText(r_postProcessEditor.hWndColorTintBValue, Str_FromFloat(glqMath::ClampFloat(Str_ToFloat(string) - 0.01f, 0.0f, 1.0f), -1));

				R_ApplyPostProcessParameters(false);

				break;
			}
		}

		break;
	}

	// Pass all unhandled messages to DefWindowProc
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

/*
 ==================
 R_CreatePostProcessEditorWindow
 ==================
*/
static void *R_CreatePostProcessEditorWindow (){

	INITCOMMONCONTROLSEX	initCommonControls;
	WNDCLASSEX				wndClass;
	RECT					rect;
	HDC						hDC;
	char					name[MAX_PATH_LENGTH];
	const char				**fileList;
	int						numFiles;
	int						screenWidth, screenHeight;
	int						x, y, w, h;
	int						size;
	int						i;

	// Get the instance handle
	r_postProcessEditor.hInstance = (HINSTANCE)Sys_GetInstanceHandle();

	// Initialize up-down control class
	initCommonControls.dwSize = sizeof(INITCOMMONCONTROLSEX);
	initCommonControls.dwICC = ICC_UPDOWN_CLASS;

	InitCommonControlsEx(&initCommonControls);

	// Calculate window position and dimensions
	screenWidth = GetSystemMetrics(SM_CXSCREEN);
	screenHeight = GetSystemMetrics(SM_CYSCREEN);

	rect.left = (screenWidth - 576) / 2;
	rect.top = (screenHeight - 322) / 2;
	rect.right = rect.left + 576;
	rect.bottom = rect.top + 322;

	AdjustWindowRectEx(&rect, POST_PROCESS_EDITOR_WINDOW_STYLE, FALSE, 0);

	x = rect.left;
	y = rect.top;
	w = rect.right - rect.left;
	h = rect.bottom - rect.top;

	// Register the window class
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.style = 0;
	wndClass.lpfnWndProc = R_PostProcessEditorWindowProc;
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = (HINSTANCE)Sys_GetInstanceHandle();
	wndClass.hIcon = (HICON)Sys_GetIconHandle();
	wndClass.hIconSm = NULL;
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = POST_PROCESS_EDITOR_WINDOW_CLASS;

	if (!RegisterClassEx(&wndClass)){
		Com_Printf(S_COLOR_RED "Could not register post-process editor window class\n");

		return NULL;
	}

	// Create the window
	r_postProcessEditor.hWnd = CreateWindowEx(0, POST_PROCESS_EDITOR_WINDOW_CLASS, POST_PROCESS_EDITOR_WINDOW_NAME, POST_PROCESS_EDITOR_WINDOW_STYLE, x, y, w, h, NULL, NULL, r_postProcessEditor.hInstance, NULL);
	if (!r_postProcessEditor.hWnd){
		UnregisterClass(POST_PROCESS_EDITOR_WINDOW_CLASS, r_postProcessEditor.hInstance);

		Com_Printf(S_COLOR_RED "Could not create post-process editor window\n");

		return NULL;
	}

	// Create the controls
	r_postProcessEditor.hWndArea = CreateWindowEx(0, "STATIC", "Map area", WS_CHILD | WS_VISIBLE | SS_RIGHT, 12, 12, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndAreaValue = CreateWindowEx(WS_EX_CLIENTEDGE, "COMBOBOX", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | CBS_DROPDOWNLIST, 116, 8, 80, 200, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndApplyToAll = CreateWindowEx(0, "BUTTON", "Apply to all areas", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 328, 7, 120, 23, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBloom = CreateWindowEx(0, "BUTTON", "Bloom", WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 8, 48, 192, 220, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBloomContrast = CreateWindowEx(0, "STATIC", "Bloom contrast", WS_CHILD | WS_VISIBLE | SS_RIGHT, 12, 79, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBloomContrastValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 116, 76, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBloomContrastSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 176, 76, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBloomThreshold = CreateWindowEx(0, "STATIC", "Bloom threshold", WS_CHILD | WS_VISIBLE | SS_RIGHT, 12, 103, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBloomThresholdValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 116, 100, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBloomThresholdSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 176, 100, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBaseIntensity = CreateWindowEx(0, "STATIC", "Base intensity", WS_CHILD | WS_VISIBLE | SS_RIGHT, 12, 127, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBaseIntensityValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 116, 124, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndBaseIntensitySpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 176, 124, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndGlowIntensity = CreateWindowEx(0, "STATIC", "Glow intensity", WS_CHILD | WS_VISIBLE | SS_RIGHT, 12, 151, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndGlowIntensityValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 116, 148, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndGlowIntensitySpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 176, 148, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorCorrection = CreateWindowEx(0, "BUTTON", "Color Correction", WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 208, 48, 360, 220, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndR = CreateWindowEx(0, "STATIC", "R", WS_CHILD | WS_VISIBLE | SS_CENTER, 316, 62, 80, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndG = CreateWindowEx(0, "STATIC", "G", WS_CHILD | WS_VISIBLE | SS_CENTER, 400, 62, 80, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndB = CreateWindowEx(0, "STATIC", "B", WS_CHILD | WS_VISIBLE | SS_CENTER, 484, 62, 80, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorShadows = CreateWindowEx(0, "STATIC", "Shadows", WS_CHILD | WS_VISIBLE | SS_RIGHT, 212, 79, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorShadowsRValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 316, 76, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorShadowsRSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 376, 76, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorShadowsGValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 400, 76, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorShadowsGSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 460, 76, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorShadowsBValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 484, 76, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorShadowsBSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 524, 76, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorHighlights = CreateWindowEx(0, "STATIC", "Highlights", WS_CHILD | WS_VISIBLE | SS_RIGHT, 212, 103, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorHighlightsRValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 316, 100, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorHighlightsRSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 376, 100, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorHighlightsGValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 400, 100, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorHighlightsGSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 460, 100, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorHighlightsBValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 484, 100, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorHighlightsBSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 524, 100, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMidtones = CreateWindowEx(0, "STATIC", "Midtones", WS_CHILD | WS_VISIBLE | SS_RIGHT, 212, 127, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMidtonesRValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 316, 124, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMidtonesRSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 376, 124, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMidtonesGValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 400, 124, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMidtonesGSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 460, 124, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMidtonesBValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 484, 124, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMidtonesBSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 524, 124, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMinOutput = CreateWindowEx(0, "STATIC", "Min output", WS_CHILD | WS_VISIBLE | SS_RIGHT, 212, 151, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMinOutputRValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 316, 148, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMinOutputRSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 376, 148, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMinOutputGValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 400, 148, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMinOutputGSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 460, 148, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMinOutputBValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 484, 148, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMinOutputBSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 524, 148, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMaxOutput = CreateWindowEx(0, "STATIC", "Max output", WS_CHILD | WS_VISIBLE | SS_RIGHT, 212, 175, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMaxOutputRValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 316, 172, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMaxOutputRSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 376, 172, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMaxOutputGValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 400, 172, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMaxOutputGSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 460, 172, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMaxOutputBValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 484, 172, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorMaxOutputBSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 524, 172, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorSaturation = CreateWindowEx(0, "STATIC", "Saturation", WS_CHILD | WS_VISIBLE | SS_RIGHT, 212, 199, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorSaturationRValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 316, 196, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorSaturationRSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 376, 196, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorSaturationGValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 400, 196, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorSaturationGSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 460, 196, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorSaturationBValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 484, 196, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorSaturationBSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 524, 196, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorTint = CreateWindowEx(0, "STATIC", "Tint", WS_CHILD | WS_VISIBLE | SS_RIGHT, 212, 223, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorTintRValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 316, 220, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorTintRSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 376, 220, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorTintGValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 400, 220, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorTintGSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 460, 220, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorTintBValue = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_RIGHT | ES_AUTOHSCROLL, 484, 220, 80, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorTintBSpin = CreateWindowEx(0, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_AUTOBUDDY, 524, 220, 20, 20, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorTable = CreateWindowEx(0, "STATIC", "Table", WS_CHILD | WS_VISIBLE | SS_RIGHT, 212, 247, 100, 14, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndColorTableValue = CreateWindowEx(WS_EX_CLIENTEDGE, "COMBOBOX", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | CBS_DROPDOWN | CBS_SORT | CBS_AUTOHSCROLL, 316, 244, 248, 200, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndApply = CreateWindowEx(0, "BUTTON", "Apply", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 8, 291, 75, 23, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndReset = CreateWindowEx(0, "BUTTON", "Reset", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 87, 291, 75, 23, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndDefaults = CreateWindowEx(0, "BUTTON", "Defaults", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 166, 291, 75, 23, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);
	r_postProcessEditor.hWndSave = CreateWindowEx(0, "BUTTON", "Save", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 493, 291, 75, 23, r_postProcessEditor.hWnd, NULL, r_postProcessEditor.hInstance, NULL);

	// Create and set the font
	hDC = GetDC(r_postProcessEditor.hWnd);
	size = -MulDiv(8, GetDeviceCaps(hDC, LOGPIXELSY), 72);
	ReleaseDC(r_postProcessEditor.hWnd, hDC);

	r_postProcessEditor.hFont = CreateFont(size, 0, 0, 0, FW_LIGHT, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, "Microsoft Sans Serif");

	SendMessage(r_postProcessEditor.hWndArea, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndAreaValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndApplyToAll, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndBloom, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndBloomContrast, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndBloomContrastValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndBloomThreshold, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndBloomThresholdValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndBaseIntensity, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndBaseIntensityValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndGlowIntensity, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndGlowIntensityValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorCorrection, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndR, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndG, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndB, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorShadows, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorShadowsRValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorShadowsGValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorShadowsBValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorHighlights, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorHighlightsRValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorHighlightsGValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorHighlightsBValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMidtones, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMidtonesRValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMidtonesGValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMidtonesBValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMinOutput, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMinOutputRValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMinOutputGValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMinOutputBValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMaxOutput, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMaxOutputRValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMaxOutputGValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorMaxOutputBValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorSaturation, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorSaturationRValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorSaturationGValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorSaturationBValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorTint, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorTintRValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorTintGValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorTintBValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorTable, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndColorTableValue, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndApply, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndReset, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndDefaults, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);
	SendMessage(r_postProcessEditor.hWndSave, WM_SETFONT, (WPARAM)r_postProcessEditor.hFont, FALSE);

	// Fill area combo box
	SendMessage(r_postProcessEditor.hWndAreaValue, CB_ADDSTRING, 0, (LPARAM)"None");

	for (i = 0; i < CM_NumAreas(); i++)
		SendMessage(r_postProcessEditor.hWndAreaValue, CB_ADDSTRING, 0, (LPARAM)Str_FromInteger(i));

	// Fill color table combo box
	SendMessage(r_postProcessEditor.hWndColorTableValue, CB_ADDSTRING, 0, (LPARAM)"_default");

	fileList = FS_ListFiles("colors", ".tga", false, &numFiles);

	for (i = 0; i < numFiles; i++){
		Str_SPrintf(name, sizeof(name), "colors/%s", fileList[i]);

		if (SendMessage(r_postProcessEditor.hWndColorTableValue, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)name) != CB_ERR)
			continue;

		SendMessage(r_postProcessEditor.hWndColorTableValue, CB_ADDSTRING, 0, (LPARAM)name);
	}

	FS_FreeFileList(fileList);

	// Set text limit for color table combo box
	SendMessage(r_postProcessEditor.hWndColorTableValue, CB_LIMITTEXT, (WPARAM)(MAX_PATH_LENGTH - 1), 0);

	// Show the window
	ShowWindow(r_postProcessEditor.hWnd, SW_SHOW);
	UpdateWindow(r_postProcessEditor.hWnd);
	SetForegroundWindow(r_postProcessEditor.hWnd);
	SetFocus(r_postProcessEditor.hWnd);

	r_postProcessEditor.initialized = true;

	return r_postProcessEditor.hWnd;
}

/*
 ==================
 R_DestroyPostProcessEditorWindow
 ==================
*/
static void R_DestroyPostProcessEditorWindow (){

	if (!r_postProcessEditor.initialized)
		return;

	if (r_postProcessEditor.hFont)
		DeleteObject(r_postProcessEditor.hFont);

	ShowWindow(r_postProcessEditor.hWnd, SW_HIDE);
	DestroyWindow(r_postProcessEditor.hWnd);

	UnregisterClass(POST_PROCESS_EDITOR_WINDOW_CLASS, r_postProcessEditor.hInstance);

	Mem_Fill(&r_postProcessEditor, 0, sizeof(postProcessEditor_t));
}

/*
 ==================
 R_EditPostProcessParameters
 ==================
*/
static void R_EditPostProcessParameters (int index, postProcessParms_t *parms){

	if (!r_postProcessEditor.initialized)
		return;

	// Apply current post-process parameters
	R_ApplyPostProcessParameters(false);

	// Set the current post-process parameters
	if (parms){
		r_postProcessEditor.enabled = true;

		r_postProcessEditor.editIndex = index;
		r_postProcessEditor.editParms = *parms;
	}
	else {
		r_postProcessEditor.enabled = false;

		parms = &r_postProcessParms;
	}

	// Update the controls
	SendMessage(r_postProcessEditor.hWndAreaValue, CB_SETCURSEL, (WPARAM)(index + 1), 0);

	SetWindowText(r_postProcessEditor.hWndBloomContrastValue, Str_FromFloat(parms->bloomContrast, -1));
	SetWindowText(r_postProcessEditor.hWndBloomThresholdValue, Str_FromFloat(parms->bloomThreshold, -1));
	SetWindowText(r_postProcessEditor.hWndBaseIntensityValue, Str_FromFloat(parms->baseIntensity, -1));
	SetWindowText(r_postProcessEditor.hWndGlowIntensityValue, Str_FromFloat(parms->glowIntensity, -1));

	SetWindowText(r_postProcessEditor.hWndColorShadowsRValue, Str_FromFloat(parms->colorShadows[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorShadowsGValue, Str_FromFloat(parms->colorShadows[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorShadowsBValue, Str_FromFloat(parms->colorShadows[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorHighlightsRValue, Str_FromFloat(parms->colorHighlights[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorHighlightsGValue, Str_FromFloat(parms->colorHighlights[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorHighlightsBValue, Str_FromFloat(parms->colorHighlights[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorMidtonesRValue, Str_FromFloat(parms->colorMidtones[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorMidtonesGValue, Str_FromFloat(parms->colorMidtones[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorMidtonesBValue, Str_FromFloat(parms->colorMidtones[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorMinOutputRValue, Str_FromFloat(parms->colorMinOutput[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorMinOutputGValue, Str_FromFloat(parms->colorMinOutput[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorMinOutputBValue, Str_FromFloat(parms->colorMinOutput[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorMaxOutputRValue, Str_FromFloat(parms->colorMaxOutput[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorMaxOutputGValue, Str_FromFloat(parms->colorMaxOutput[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorMaxOutputBValue, Str_FromFloat(parms->colorMaxOutput[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorSaturationRValue, Str_FromFloat(parms->colorSaturation[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorSaturationGValue, Str_FromFloat(parms->colorSaturation[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorSaturationBValue, Str_FromFloat(parms->colorSaturation[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorTintRValue, Str_FromFloat(parms->colorTint[0], -1));
	SetWindowText(r_postProcessEditor.hWndColorTintGValue, Str_FromFloat(parms->colorTint[1], -1));
	SetWindowText(r_postProcessEditor.hWndColorTintBValue, Str_FromFloat(parms->colorTint[2], -1));

	SetWindowText(r_postProcessEditor.hWndColorTableValue, parms->colorTableName);

	// Enable or disable the controls
	EnableWindow(r_postProcessEditor.hWndApplyToAll, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBloom, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBloomContrast, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBloomContrastValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBloomContrastSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBloomThreshold, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBloomThresholdValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBloomThresholdSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBaseIntensity, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBaseIntensityValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndBaseIntensitySpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndGlowIntensity, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndGlowIntensityValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndGlowIntensitySpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorCorrection, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndR, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndG, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndB, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorShadows, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorShadowsRValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorShadowsRSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorShadowsGValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorShadowsGSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorShadowsBValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorShadowsBSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorHighlights, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorHighlightsRValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorHighlightsRSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorHighlightsGValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorHighlightsGSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorHighlightsBValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorHighlightsBSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMidtones, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMidtonesRValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMidtonesRSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMidtonesGValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMidtonesGSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMidtonesBValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMidtonesBSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMinOutput, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMinOutputRValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMinOutputRSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMinOutputGValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMinOutputGSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMinOutputBValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMinOutputBSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMaxOutput, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMaxOutputRValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMaxOutputRSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMaxOutputGValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMaxOutputGSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMaxOutputBValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorMaxOutputBSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorSaturation, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorSaturationRValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorSaturationRSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorSaturationGValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorSaturationGSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorSaturationBValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorSaturationBSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorTint, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorTintRValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorTintRSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorTintGValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorTintGSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorTintBValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorTintBSpin, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorTable, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndColorTableValue, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndApply, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndReset, r_postProcessEditor.enabled);
	EnableWindow(r_postProcessEditor.hWndDefaults, r_postProcessEditor.enabled);
}

#else

/*
 ==================
 R_CreatePostProcessEditorWindow
 ==================
*/
static void *R_CreatePostProcessEditorWindow (){

	Com_Printf(S_COLOR_RED "The post-process editor is not currently supported on this platform");

	return NULL;
}

/*
 ==================
 R_DestroyPostProcessEditorWindow
 ==================
*/
static void R_DestroyPostProcessEditorWindow (){

}

/*
 ==================
 R_EditPostProcessParameters
 ==================
*/
static void R_EditPostProcessParameters (int index, postProcessParms_t *parms){

}

#endif


// ============================================================================


/*
 ==================
 R_ComparePostProcess
 ==================
*/
static bool R_ComparePostProcess (const postProcessParms_t *postProcessParms1, const postProcessParms_t *postProcessParms2){

	if (glqMath::FAbs(postProcessParms1->bloomContrast - postProcessParms2->bloomContrast) > POST_PROCESS_EQUAL_EPSILON)
		return false;
	if (glqMath::FAbs(postProcessParms1->bloomThreshold - postProcessParms2->bloomThreshold) > POST_PROCESS_EQUAL_EPSILON)
		return false;
	if (glqMath::FAbs(postProcessParms1->baseIntensity - postProcessParms2->baseIntensity) > POST_PROCESS_EQUAL_EPSILON)
		return false;
	if (glqMath::FAbs(postProcessParms1->glowIntensity - postProcessParms2->glowIntensity) > POST_PROCESS_EQUAL_EPSILON)
		return false;
	if (!postProcessParms1->colorShadows.Compare(postProcessParms2->colorShadows, POST_PROCESS_EQUAL_EPSILON))
		return false;
	if (!postProcessParms1->colorHighlights.Compare(postProcessParms2->colorHighlights, POST_PROCESS_EQUAL_EPSILON))
		return false;
	if (!postProcessParms1->colorMidtones.Compare(postProcessParms2->colorMidtones, POST_PROCESS_EQUAL_EPSILON))
		return false;
	if (!postProcessParms1->colorMinOutput.Compare(postProcessParms2->colorMinOutput, POST_PROCESS_EQUAL_EPSILON))
		return false;
	if (!postProcessParms1->colorMaxOutput.Compare(postProcessParms2->colorMaxOutput, POST_PROCESS_EQUAL_EPSILON))
		return false;
	if (!postProcessParms1->colorSaturation.Compare(postProcessParms2->colorSaturation, POST_PROCESS_EQUAL_EPSILON))
		return false;
	if (!postProcessParms1->colorTint.Compare(postProcessParms2->colorTint, POST_PROCESS_EQUAL_EPSILON))
		return false;
	if (Str_ICompare(postProcessParms1->colorTableName, postProcessParms2->colorTableName))
		return false;

	return true;
}

/*
 ==================
 R_UpdatePostProcess
 ==================
*/
void R_UpdatePostProcess (){

	postProcessParms_t	*postProcessParms;
	postProcessParms_t	*previous, *current;
	float				frac;

	if (!r_postProcess->integerValue)
		return;

	if (!rg.viewParms.primaryView || rg.viewParms.viewType != VIEW_MAIN)
		return;

	// Update post-process parameters
	if (rg.viewArea == -1)
		postProcessParms = &rg.postProcess.postProcessGeneric;
	else
		postProcessParms = &rg.postProcess.postProcessList[rg.viewArea];

	if (!rg.postProcess.current){
		rg.postProcess.time = rg.renderView.time;

		Mem_Copy(&rg.postProcess.previous, postProcessParms, sizeof(postProcessParms_t));
		rg.postProcess.current = postProcessParms;
	}
	else if (rg.postProcess.current != postProcessParms){
		if (R_ComparePostProcess(rg.postProcess.current, postProcessParms))
			rg.postProcess.current = postProcessParms;
		else {
			rg.postProcess.time = rg.renderView.time;

			Mem_Copy(&rg.postProcess.previous, &rg.postProcess.postProcessParms, sizeof(postProcessParms_t));
			rg.postProcess.current = postProcessParms;
		}
	}

	// Interpolate
	postProcessParms = &rg.postProcess.postProcessParms;

	previous = &rg.postProcess.previous;
	current = rg.postProcess.current;

	if (!r_postProcessTime->floatValue)
		frac = 1.0f;
	else
		frac = MS2SEC(rg.renderView.time - rg.postProcess.time) / r_postProcessTime->floatValue;

	if (frac <= 0.0f)
		Mem_Copy(postProcessParms, previous, sizeof(postProcessParms_t));
	else if (frac >= 1.0f)
		Mem_Copy(postProcessParms, current, sizeof(postProcessParms_t));
	else {
		postProcessParms->bloomContrast = glqMath::LerpFast(previous->bloomContrast, current->bloomContrast, frac);
		postProcessParms->bloomThreshold = glqMath::LerpFast(previous->bloomThreshold, current->bloomThreshold, frac);
		postProcessParms->baseIntensity = glqMath::LerpFast(previous->baseIntensity, current->baseIntensity, frac);
		postProcessParms->glowIntensity = glqMath::LerpFast(previous->glowIntensity, current->glowIntensity, frac);
		postProcessParms->colorShadows.LerpFast(previous->colorShadows, current->colorShadows, frac);
		postProcessParms->colorHighlights.LerpFast(previous->colorHighlights, current->colorHighlights, frac);
		postProcessParms->colorMidtones.LerpFast(previous->colorMidtones, current->colorMidtones, frac);
		postProcessParms->colorMinOutput.LerpFast(previous->colorMinOutput, current->colorMinOutput, frac);
		postProcessParms->colorMaxOutput.LerpFast(previous->colorMaxOutput, current->colorMaxOutput, frac);
		postProcessParms->colorSaturation.LerpFast(previous->colorSaturation, current->colorSaturation, frac);
		postProcessParms->colorTint.LerpFast(previous->colorTint, current->colorTint, frac);

		// Blend the color tables
		R_BlendColorTables(postProcessParms->colorTable, previous->colorTable, current->colorTable, frac);
	}

	// Update the color table texture
	R_UploadTextureImage(rg.colorTableTexture, 0, (const byte *)postProcessParms->colorTable, 256, 1);
}


/*
 ==============================================================================

 INITIALIZATION & SHUTDOWN

 ==============================================================================
*/


/*
 ==================
 R_InitPostProcess
 ==================
*/
void R_InitPostProcess (){

	int		i;

	// Add commands
	Cmd_AddCommand("editPostProcess", R_EditPostProcess_f, "Launches the integrated post-process editor", NULL);

	// Load the default color table
	R_LoadColorTable(r_postProcessParmsGeneric.colorTableName, r_postProcessParmsGeneric.colorTable);

	// Fill in defaults
	Mem_Copy(&rg.postProcess.postProcessGeneric, &r_postProcessParmsGeneric, sizeof(postProcessParms_t));

	for (i = 0; i < MAX_POST_PROCESS_AREAS; i++)
		Mem_Copy(&rg.postProcess.postProcessList[i], &r_postProcessParmsGeneric, sizeof(postProcessParms_t));

	Mem_Copy(&rg.postProcess.postProcessParms, &r_postProcessParmsGeneric, sizeof(postProcessParms_t));

	// Clear interpolation parameters
	rg.postProcess.time = 0;

	Mem_Copy(&rg.postProcess.previous, &r_postProcessParmsGeneric, sizeof(postProcessParms_t));
	rg.postProcess.current = NULL;
}

/*
 ==================
 R_ShutdownPostProcess
 ==================
*/
void R_ShutdownPostProcess (){

	// Remove commands
	Cmd_RemoveCommand("editPostProcess");

	// Close the post-process editor if active
	if (rg.postProcess.editing)
		Com_CloseEditor();
}